﻿using express.bttree;

namespace express.compiler;

/**
 * 语法分析
 * --------------------------------
 * Or      ->    And OrTail
 * OrTail  ->    + And OrTail
 *           |     - And OrTail
 *           |     null
 *
 * And      ->    BracketOrItems AndTail
 * AndTail  ->    * BracketOrItems AndTail
 *           |     / BracketOrItems AndTail
 *           |     null
 *
 * bracketOrItems    ->    (Or)
 *           |              Items
 */
public class Parser
{
    private List<String> tokens;
    private String currToken = "";
    private int currIndex = -1;


    private Boolean IsAnd()
    {
        var token = this.currToken.ToLower();
        return string.Equals(token, "and") ||
               string.Equals(token, "&&") ||
               string.Equals(token, "&") ||
               string.Equals(token, "*") ||
               string.Equals(token, "/");
    }

    private Boolean IsOr()
    {
        var token = this.currToken.ToLower();
        return string.Equals(token, "or") ||
               string.Equals(token, "||") ||
               string.Equals(token, "|") ||
               string.Equals(token, "+") ||
               string.Equals(token, "-");
    }

    private Boolean IsOp()
    {
        return Utils.IsOp(this.currToken);
    }


    private bool IsStartParam()
    {
        bool ok = string.Equals(this.currToken, Keywords.PARAM_START);
        return ok;
    }

    private bool IsEndParam()
    {
        bool ok = string.Equals(this.currToken, Keywords.PARAM_END);
        return ok;
    }


    private Boolean IsStartBracket()
    {
        return string.Equals(this.currToken, "(");
    }

    private Boolean IsEndBracket()
    {
        return string.Equals(this.currToken, ")");
    }

    private Boolean IsItem()
    {
        var token = this.currToken.ToLower();
        return !string.Equals(token, "and") &&
               !string.Equals(token, "or") &&
               !string.Equals(token, "(") &&
               !string.Equals(token, ")") &&
               !string.Equals(token, "&") &&
               !string.Equals(token, "&&") &&
               !string.Equals(token, "|") &&
               !string.Equals(token, "||") &&
               !string.Equals(token, "+") &&
               !string.Equals(token, "-") &&
               !string.Equals(token, "*") &&
               !string.Equals(token, "/") &&
               !string.Equals(token, "") &&
               !string.Equals(token, Keywords.PARAM_START) &&
               !string.Equals(token, Keywords.PARAM_END);
    }

    private Boolean IsEnd()
    {
        return this.currIndex >= this.tokens.Count();
    }

    private void PrintLog(String log)
    {
        //Console.WriteLine("处理：" + log);
    }

    public void MoveNext()
    {
        if (currIndex >= this.tokens.Count())
        {
            throw new Exception("move next error");
        }

        if (currIndex >= this.tokens.Count() - 1)
        {
            this.currIndex++;
            this.currToken = "";
            return;
        }
        currIndex++;
        this.currToken = this.tokens[currIndex];
    }

    private Exception newException()
    {
        return new Exception("不处理该关键字，token:" + this.currToken);
    }

    public Parser(List<String> tokens)
    {
        this.tokens = tokens;
    }

    public SyntaxNode parse()
    {
        SyntaxNode root = new SyntaxNode(SyntaxNodeType.Nodes);
        MoveNext();
        if (Or(root) && IsEnd())
        {
            return root;
        }
        else
        {
            return null;
        }
    }

    private Boolean Or(SyntaxNode parent)
    {
        SyntaxNode andExpr = new SyntaxNode(SyntaxNodeType.Nodes);
        parent.addChild(andExpr);
        if (!And(andExpr))
        {
            return false;
        }
        else
        {
            return OrTail(parent);
        }
    }

    private Boolean OrTail(SyntaxNode parent)
    {
        if (this.IsOr())
        {
            SyntaxNode opNode = new SyntaxNode(SyntaxNodeType.Or);
            opNode.Value = this.currToken;
            parent.addChild(opNode);

            PrintLog(this.currToken);
            this.MoveNext();

            SyntaxNode andNode = new SyntaxNode(SyntaxNodeType.Nodes);
            parent.addChild(andNode);

            if (!And(andNode))
            {
                return false;
            }
            else
            {
                return OrTail(parent);
            }
        }
        else
        {
            return true;
        }
    }

    private Boolean And(SyntaxNode parent)
    {
        if (!bracketOrItems(parent))
        {
            return false;
        }
        else
        {
            return AndTail(parent);
        }
    }

    private bool AndTail(SyntaxNode parent)
    {
        if (this.IsAnd())
        {
            SyntaxNode opNode = new SyntaxNode(SyntaxNodeType.And);
            opNode.Value = this.currToken;
            parent.addChild(opNode);

            PrintLog(this.currToken);
            MoveNext();

            if (!bracketOrItems(parent))
            {
                return false;
            }
            else
            {
                return AndTail(parent);
            }
        }
        else
        {
            return true;
        }
    }

    private Boolean bracketOrItems(SyntaxNode parent)
    {
        if (this.IsStartBracket())
        {
            this.MoveNext();
            this.PrintLog("(");
            SyntaxNode bracketNode = new SyntaxNode(SyntaxNodeType.Nodes);
            parent.addChild(bracketNode);
            if (!Or(bracketNode))
            {
                return false;
            }
            else
            {
                if (!this.IsEndBracket())
                {
                    throw newException();
                }
                this.MoveNext();
                this.PrintLog(")");
                return true;
            }
        }
        else if (this.IsItem())
        {
            SyntaxNode exprNode = new SyntaxNode(SyntaxNodeType.Expr);
            parent.addChild(exprNode);
            if (!Item(exprNode))
            {
                return false;
            }
            else
            {
                return ItemTail(exprNode);
            }
        }
        else
        {
            throw newException();
        }
    }

    private Boolean Item(SyntaxNode parent)
    {
        PrintLog("item:" + this.currToken);
        SyntaxNode itemNode = new SyntaxNode(SyntaxNodeType.Item, this.currToken);
        parent.addChild(itemNode);
        this.MoveNext();
        return true;
    }

    private Boolean ItemTail(SyntaxNode expr)
    {
        if (this.IsItem())
        {
            if (!Item(expr))
            {
                return false;
            }
            else
            {
                return ItemTail(expr);
            }
        }
        else if (this.IsStartParam())
        {
            //参数列表之前只能有一个项目
            if (expr.ChildNodes.Count() != 1)
            {
                throw newException();
            }
            if (!paramList(expr))
            {
                return false;
            }
            else
            {
                //参数列表结束后，如果接下来不符合这些规则，则表达式不合法
                if (!this.IsEnd() && !this.IsOp() && !this.IsAnd() && !this.IsOr())
                {
                    throw newException();
                }
                return ItemTail(expr);
            }
        }
        else
        {
            return true;
        }
    }


    private Boolean paramList(SyntaxNode parent)
    {
        if (this.IsStartParam())
        {
            PrintLog("(");
            MoveNext();
            //直接闭合
            if (this.IsEndParam())
            {
                PrintLog(")");
                MoveNext();
                return true;
            }

            if (!Item(parent))
            {
                return false;
            }
            else
            {
                return ParamListTail(parent);
            }
        }
        else
        {
            throw newException();
        }
    }

    private Boolean ParamListTail(SyntaxNode parent)
    {
        if (IsItem())
        {
            if (!Item(parent))
            {
                return false;
            }
            else
            {
                return ParamListTail(parent);
            }
        }
        else if (this.IsEndParam())
        {
            PrintLog(Keywords.PARAM_END);
            MoveNext();
            return true;
        }
        else
        {
            throw newException();
        }
    }
}